home *** CD-ROM | disk | FTP | other *** search
/ Micromanía 92 / CDMM92_1.ISO / SOF 2 SDK / sof2sdk-101.msi / _92D6AC311BB48EBA344BBABC89DA6AB0 / _3A30DDAF8C3444DC9AA0BD26B09E379D < prev    next >
Encoding:
Text File  |  2002-06-13  |  23.7 KB  |  1,062 lines

  1. // Copyright (C) 2001-2002 Raven Software
  2. //
  3. // cg_servercmds.c -- reliably sequenced text commands sent by the server
  4. // these are processed at snapshot transition time, so there will definately
  5. // be a valid snapshot this frame
  6.  
  7. #include "cg_local.h"
  8. #include "../../ui/menudef.h"
  9. #if !defined(CL_LIGHT_H_INC)
  10.     #include "cg_lights.h"
  11. #endif
  12.  
  13. /*
  14. =================
  15. CG_ParseScores
  16. =================
  17. */
  18. static void CG_ParseScores( void ) 
  19. {
  20.     int        i;
  21.  
  22.     cg.scoreBoardSpectators[0] = '\0';
  23.  
  24.     cg.numScores = atoi( CG_Argv( 1 ) );
  25.     if ( cg.numScores > MAX_CLIENTS ) 
  26.     {
  27.         cg.numScores = MAX_CLIENTS;
  28.     }
  29.  
  30.     cg.teamScores[0] = atoi( CG_Argv( 2 ) );
  31.     cg.teamScores[1] = atoi( CG_Argv( 3 ) );
  32.  
  33.     memset( cg.scores, 0, sizeof( cg.scores ) );
  34.     for ( i = 0 ; i < cg.numScores ; i++ ) 
  35.     {
  36.         cg.scores[i].client = atoi( CG_Argv( i * 9 + 4 ) );
  37.         cg.scores[i].score = atoi( CG_Argv( i * 9 + 5 ) );
  38.         cg.scores[i].kills = atoi( CG_Argv( i * 9 + 6 ) );
  39.         cg.scores[i].deaths = atoi( CG_Argv( i * 9 + 7 ) );
  40.         cg.scores[i].ping = atoi( CG_Argv( i * 9 + 8 ) );
  41.         cg.scores[i].time = atoi( CG_Argv( i * 9 + 9 ) );
  42.         cgs.clientinfo[ cg.scores[i].client ].ghost = atoi( CG_Argv( i * 9 + 10 ) );
  43.         cgs.clientinfo[ cg.scores[i].client ].gametypeitems = atoi( CG_Argv( i * 9 + 11 ) );
  44.         cg.scores[i].teamkillDamage = atoi( CG_Argv( i * 9 + 12 ) );
  45.  
  46.         if ( cg.scores[i].client < 0 || cg.scores[i].client >= MAX_CLIENTS )
  47.         {
  48.             cg.scores[i].client = 0;
  49.         }
  50.  
  51.         if ( cg.scores[i].ping < 0 )
  52.         {
  53.             cg.scores[i].time = 0;
  54.         }
  55.  
  56.         cgs.clientinfo[ cg.scores[i].client ].score = cg.scores[i].score;
  57.         cg.scores[i].team = cgs.clientinfo[cg.scores[i].client].team;
  58.  
  59.         if ( cg.scores[i].team == TEAM_SPECTATOR )
  60.         {
  61.             if ( cg.scoreBoardSpectators[0] )
  62.             {
  63.                 strcat ( cg.scoreBoardSpectators, ", " );
  64.             }
  65.  
  66.             strcat ( cg.scoreBoardSpectators, va("%s (%d)", cgs.clientinfo[cg.scores[i].client].name, cg.scores[i].ping ) );
  67.         }
  68.     }
  69. }
  70.  
  71. /*
  72. ================
  73. CG_ParseServerinfo
  74.  
  75. This is called explicitly when the gamestate is first received,
  76. and whenever the server updates any serverinfo flagged cvars
  77. ================
  78. */
  79. void CG_ParseServerinfo( void ) 
  80. {
  81.     const char    *info;
  82.     char        *mapname;    
  83.  
  84.     info = CG_ConfigString( CS_SERVERINFO );
  85.     cgs.gametype = BG_FindGametype ( Info_ValueForKey( info, "g_gametype" ) );
  86.     cgs.gametypeData = &bg_gametypeData[cgs.gametype];
  87.     cgs.dmflags = atoi( Info_ValueForKey( info, "dmflags" ) );
  88.     cgs.teamflags = atoi( Info_ValueForKey( info, "teamflags" ) );
  89.     cgs.scorelimit = atoi( Info_ValueForKey( info, "scorelimit" ) );
  90.     cgs.timelimit = atoi( Info_ValueForKey( info, "timelimit" ) );
  91.     cgs.maxclients = atoi( Info_ValueForKey( info, "sv_maxclients" ) );
  92.     cgs.friendlyFire = atoi( Info_ValueForKey( info, "g_friendlyFire" ) ) ? qtrue : qfalse;
  93.     mapname = Info_ValueForKey( info, "mapname" );
  94.     Com_sprintf( cgs.mapname, sizeof( cgs.mapname ), "maps/%s.bsp", mapname );
  95.  
  96.     trap_Cvar_Set ( "ui_about_gametype", va("%i", cgs.gametype ) );
  97.     trap_Cvar_Set ( "ui_about_gametypename", cgs.gametypeData->displayName );
  98.     trap_Cvar_Set ( "ui_about_scorelimit", va("%i", cgs.scorelimit ) );
  99.     trap_Cvar_Set ( "ui_about_timelimit", va("%i", cgs.timelimit ) );
  100.     trap_Cvar_Set ( "ui_about_maxclients", va("%i", cgs.maxclients ) );
  101.     trap_Cvar_Set ( "ui_about_dmflags", va("%i", cgs.dmflags ) );
  102.     trap_Cvar_Set ( "ui_about_mapname", mapname );
  103.     trap_Cvar_Set ( "ui_about_hostname", Info_ValueForKey( info, "sv_hostname" ) );
  104.     trap_Cvar_Set ( "ui_about_needpass", Info_ValueForKey( info, "g_needpass" ) );
  105.     trap_Cvar_Set ( "ui_about_botminplayers", Info_ValueForKey ( info, "bot_minplayers" ) );
  106.     trap_Cvar_Set ( "ui_info_availableweapons", Info_ValueForKey ( info, "g_availableWeapons" ) );
  107.     trap_Cvar_Set ( "ui_info_teamgame", va("%i", cgs.gametypeData->teams ? 1 : 0 ) );
  108.     
  109.     BG_SetAvailableOutfitting ( Info_ValueForKey ( info, "g_availableWeapons" ) );
  110.  
  111.     if ( cgs.gametypeData->teams )
  112.     {
  113.         trap_Cvar_Set ( "ui_info_redteam", CG_ConfigString ( CS_GAMETYPE_REDTEAM ) );
  114.         trap_Cvar_Set ( "ui_info_blueteam", CG_ConfigString ( CS_GAMETYPE_BLUETEAM ) );
  115.     }
  116.     else
  117.     {
  118.         trap_Cvar_Set ( "ui_info_redteam", "" );
  119.         trap_Cvar_Set ( "ui_info_blueteam", "" );
  120.     }
  121.  
  122.     info = CG_ConfigString( CS_TERRAINS + 1 );
  123.     if ( !info || !*info )
  124.     {
  125.         cg.mInRMG = qfalse;
  126.     }
  127.     else
  128.     {
  129.         cg.mInRMG = qtrue;
  130.     }
  131. }
  132.  
  133. /*
  134. ==================
  135. CG_ParseWarmup
  136. ==================
  137. */
  138. static void CG_ParseWarmup( void ) 
  139. {
  140.     const char    *info;
  141.     int            warmup;
  142.  
  143.     info = CG_ConfigString( CS_WARMUP );
  144.  
  145.     warmup = atoi( info );
  146.     cg.warmupCount = -1;
  147.  
  148.     cg.warmup = warmup;
  149. }
  150.  
  151. static void CG_ParseGametypeTimer ( void )
  152. {
  153.     cgs.gametypeTimerTime = atoi( CG_ConfigString( CS_GAMETYPE_TIMER ) );
  154. }
  155.  
  156. static void CG_ParseGametypeMessage ( void )
  157. {
  158.     char  temp[1024];
  159.     char* comma;
  160.  
  161.     strcpy ( temp, CG_ConfigString( CS_GAMETYPE_MESSAGE ) );
  162.     comma = strchr ( temp, ',' );
  163.     if ( !comma )
  164.     {
  165.         return;
  166.     }
  167.  
  168.     *(comma++) = '\0';
  169.  
  170.     cgs.gametypeMessageTime = atoi ( temp );
  171.  
  172.     // Silent gametype message
  173.     if ( *comma == '@' )
  174.     {
  175.         strcpy ( cgs.gametypeMessage, comma + 1 );
  176.     }
  177.     else
  178.     {
  179.         strcpy ( cgs.gametypeMessage, comma );
  180.         Com_Printf ( "@%s\n", cgs.gametypeMessage );    
  181.     }
  182. }
  183.  
  184. /*
  185. ================
  186. CG_ParseVoteTime
  187. ================
  188. */
  189. static void CG_ParseVoteTime ( void )
  190. {
  191.     char  temp[1024];
  192.     char* comma;
  193.     const char *str;
  194.  
  195.     str = CG_ConfigString( CS_VOTE_TIME );
  196.  
  197.     strcpy ( temp, str );
  198.     comma = strchr ( temp, ',' );
  199.     if ( !comma )
  200.     {
  201.         cgs.voteTime = cgs.voteDuration = 0;
  202.         return;
  203.     }
  204.     *comma = 0;
  205.  
  206.     cgs.voteTime = atoi(str);
  207.     cgs.voteDuration = atoi(comma+1);
  208.     cgs.voteModified = qtrue;
  209. }
  210.  
  211. /*
  212. ================
  213. CG_SetConfigValues
  214.  
  215. Called on load to set the initial values from configure strings
  216. ================
  217. */
  218. void CG_SetConfigValues( void ) 
  219. {
  220.     int i;
  221.  
  222.     cgs.levelStartTime      = atoi( CG_ConfigString( CS_LEVEL_START_TIME ) );
  223.     cg.warmup              = atoi( CG_ConfigString( CS_WARMUP ) );
  224.     cgs.pickupsDisabled   = atoi( CG_ConfigString( CS_PICKUPSDISABLED ) );
  225.     cgs.gameID              = atoi( CG_ConfigString( CS_GAME_ID ) );
  226.  
  227.     trap_Cvar_Set ( "ui_info_pickupsdisabled", va("%i", cgs.pickupsDisabled ) );
  228.  
  229.     CG_ParseGametypeTimer ( );
  230.     CG_ParseGametypeMessage ( );
  231.     CG_ParseVoteTime ( );
  232.  
  233.     for ( i = 0; i < MAX_HUDICONS; i ++ )
  234.     {
  235.         cgs.hudIcons[i] = atoi ( CG_ConfigString ( CS_HUDICONS + i ) );
  236.     }
  237. }
  238.  
  239. /*
  240. =====================
  241. CG_ShaderStateChanged
  242. =====================
  243. */
  244. void CG_ShaderStateChanged(void) {
  245.     char originalShader[MAX_QPATH];
  246.     char newShader[MAX_QPATH];
  247.     char timeOffset[16];
  248.     const char *o;
  249.     char *n,*t;
  250.  
  251.     o = CG_ConfigString( CS_SHADERSTATE );
  252.     while (o && *o) {
  253.         n = strstr(o, "=");
  254.         if (n && *n) {
  255.             strncpy(originalShader, o, n-o);
  256.             originalShader[n-o] = 0;
  257.             n++;
  258.             t = strstr(n, ":");
  259.             if (t && *t) {
  260.                 strncpy(newShader, n, t-n);
  261.                 newShader[t-n] = 0;
  262.             } else {
  263.                 break;
  264.             }
  265.             t++;
  266.             o = strstr(t, "@");
  267.             if (o) {
  268.                 strncpy(timeOffset, t, o-t);
  269.                 timeOffset[o-t] = 0;
  270.                 o++;
  271.                 trap_R_RemapShader( originalShader, newShader, timeOffset );
  272.             }
  273.         } else {
  274.             break;
  275.         }
  276.     }
  277. }
  278.  
  279. /*
  280. ================
  281. CG_ConfigStringModified
  282. ================
  283. */
  284. static void CG_ConfigStringModified( void ) 
  285. {
  286.     const char    *str;
  287.     int            num;
  288.  
  289.     num = atoi( CG_Argv( 1 ) );
  290.  
  291.     // get the gamestate from the client system, which will have the
  292.     // new configstring already integrated
  293.     trap_GetGameState( &cgs.gameState );
  294.  
  295.     // look up the individual string that was modified
  296.     str = CG_ConfigString( num );
  297.  
  298.     // do something with it if necessary
  299.     if ( num == CS_MUSIC ) 
  300.     {
  301.         CG_StartMusic( qtrue );
  302.     } 
  303.     else if ( num == CS_SERVERINFO ) 
  304.     {
  305.         CG_ParseServerinfo();
  306.     } 
  307.     else if ( num == CS_WARMUP ) 
  308.     {
  309.         CG_ParseWarmup();
  310.     } 
  311.     else if ( num == CS_GAMETYPE_TIMER )
  312.     {
  313.         CG_ParseGametypeTimer ( );
  314.     }
  315.     else if ( num == CS_GAMETYPE_MESSAGE )
  316.     {
  317.         CG_ParseGametypeMessage ( );
  318.     }
  319.     else if ( num == CS_LEVEL_START_TIME ) 
  320.     {
  321.         cgs.levelStartTime = atoi( str );
  322.     } 
  323.     else if ( num == CS_VOTE_TIME ) 
  324.     {
  325.         CG_ParseVoteTime();
  326.     } 
  327.     else if ( num == CS_VOTE_NEEDED )
  328.     {
  329.         cgs.voteNeeded = atoi( str );
  330.         cgs.voteModified = qtrue;
  331.     }
  332.     else if ( num == CS_VOTE_YES ) 
  333.     {
  334.         cgs.voteYes = atoi( str );
  335.         cgs.voteModified = qtrue;
  336.     } 
  337.     else if ( num == CS_VOTE_NO ) 
  338.     {
  339.         cgs.voteNo = atoi( str );
  340.         cgs.voteModified = qtrue;
  341.     } 
  342.     else if ( num == CS_VOTE_STRING ) 
  343.     {
  344.         Q_strncpyz( cgs.voteString, str, sizeof( cgs.voteString ) );
  345.         trap_S_StartLocalSound( cgs.media.talkSound, CHAN_ANNOUNCER );
  346.     } 
  347.     else if ( num == CS_INTERMISSION ) 
  348.     {
  349.         cg.intermissionStarted = atoi( str );
  350.     } 
  351.     else if ( num >= CS_MODELS && num < CS_MODELS+MAX_MODELS ) 
  352.     {
  353.         cgs.gameModels[ num-CS_MODELS ] = trap_R_RegisterModel( str );
  354.     } 
  355.     else if ( num >= CS_SOUNDS && num < CS_SOUNDS+MAX_MODELS ) 
  356.     {
  357.         if ( str[0] != '*' ) 
  358.         {    
  359.             // player specific sounds don't register here
  360.             cgs.gameSounds[ num-CS_SOUNDS] = trap_S_RegisterSound( str );
  361.         }
  362.     } 
  363.     else if ( num >= CS_PLAYERS && num < CS_PLAYERS+MAX_CLIENTS ) 
  364.     {
  365.         CG_NewClientInfo( num - CS_PLAYERS );
  366.     } 
  367.     else if ( num == CS_SHADERSTATE ) 
  368.     {
  369.         CG_ShaderStateChanged();
  370.     }
  371.     else if ( num >= CS_LIGHT_STYLES && num < CS_LIGHT_STYLES + (MAX_LIGHT_STYLES * 3))
  372.     {
  373.         CG_SetLightstyle(num - CS_LIGHT_STYLES);
  374.     }
  375.     else if ( num >= CS_ICONS && num < CS_ICONS + MAX_ICONS )
  376.     {
  377.         cgs.gameIcons[ num - CS_ICONS ] = trap_R_RegisterShaderNoMip ( str );
  378.     }
  379.     else if ( num >= CS_HUDICONS && num < CS_HUDICONS + MAX_HUDICONS )
  380.     {
  381.         cgs.hudIcons[ num - CS_HUDICONS ] = atoi ( str );
  382.     }
  383. }
  384.  
  385.  
  386. /*
  387. =======================
  388. CG_AddChatText
  389.  
  390. Adds chat text to the chat chat buffer
  391. =======================
  392. */
  393. static void CG_AddChatText ( int client, const char *str ) 
  394. {
  395.     int        len;
  396.     char    *p;
  397.     char    *ls;
  398.     int        lastcolor;
  399.     int        chatHeight;
  400.     float   w;
  401.  
  402.     if ( client >= 0 )
  403.     {
  404.         cgs.clientinfo[client].mLastChatTime = cg.time;
  405.     }
  406.  
  407.     // Grab the users chat height settings
  408.     chatHeight = cg_chatHeight.integer;
  409.     if ( chatHeight > CHAT_HEIGHT )
  410.     {
  411.         chatHeight = CHAT_HEIGHT;
  412.     }
  413.  
  414.     // Chats disabled?
  415.     if ( chatHeight <= 0 || cg_chatTime.integer <= 0 ) 
  416.     {
  417.         cgs.chatPos = cgs.chatLastPos = 0;
  418.         return;
  419.     }
  420.  
  421.     len = 0;
  422.  
  423.     lastcolor = COLOR_WHITE;
  424.  
  425.     // Next position to write chat text too in the circular chat buffer    
  426.     p = cgs.chatText[cgs.chatPos % chatHeight];
  427.     *p = 0;
  428.  
  429.     ls = NULL;
  430.     w  = 0;
  431.  
  432.     while (*str) 
  433.     {
  434.         float cw = trap_R_GetTextWidth ( va("%c",*str), cgs.media.hudFont, 0.43f, 0 );
  435.  
  436.         if ( w > 560 )
  437.         {
  438.             w = 0;
  439.  
  440.             if (ls) 
  441.             {
  442.                 str -= (p - ls);
  443.                 str++;
  444.                 p -= (p - ls);
  445.             }
  446.  
  447.             *p = 0;
  448.  
  449.             cgs.chatTime[cgs.chatPos % chatHeight] = cg.time;
  450.  
  451.             cgs.chatPos++;
  452.             p = cgs.chatText[ cgs.chatPos % chatHeight ];
  453.             *p = 0;
  454.             *p++ = Q_COLOR_ESCAPE;
  455.             *p++ = lastcolor;
  456.             len = 0;
  457.             ls = NULL;
  458.         }
  459.  
  460.         if ( Q_IsColorString( str ) ) 
  461.         {
  462.             *p++ = *str++;
  463.             lastcolor = *str;
  464.             *p++ = *str++;
  465.             continue;
  466.         }
  467.  
  468.         if (*str == ' ') 
  469.         {
  470.             ls = p;
  471.         }
  472.  
  473.         *p++ = *str++;
  474.         len++;
  475.         w += cw;
  476.     }
  477.     *p = 0;
  478.  
  479.     cgs.chatTime[ cgs.chatPos % chatHeight] = cg.time;
  480.     cgs.chatPos++;
  481.  
  482.     if (cgs.chatPos - cgs.chatLastPos > chatHeight)
  483.     {
  484.         cgs.chatLastPos = cgs.chatPos - chatHeight;
  485.     }
  486. }
  487.  
  488. /*
  489. ===============
  490. CG_MapRestart
  491.  
  492. The server has issued a map_restart, so the next snapshot
  493. is completely new and should not be interpolated to.
  494.  
  495. A map restart will clear everything, but doesn't
  496. require a reload of all the media
  497. ===============
  498. */
  499. void CG_MapRestart( qboolean gametypeRestart ) 
  500. {
  501.     if ( cg_showmiss.integer ) 
  502.     {
  503.         Com_Printf( "CG_MapRestart\n" );
  504.     }
  505.  
  506.     trap_R_ClearDecals ( );
  507.     trap_FX_Reset ( );
  508.     trap_MAT_Reset();
  509.  
  510.     CG_InitLocalEntities();
  511.  
  512.     cg.intermissionStarted = qfalse;
  513.  
  514.     // dont clear votes on gametype restarts
  515.     if ( !gametypeRestart )
  516.     {
  517.         cgs.voteTime = 0;
  518.         cgs.gametypeMessage[0] = '\0';
  519.         cgs.gametypeMessageTime = 0;
  520.     }
  521.  
  522.     cgs.gameover[0] = '\0';
  523.  
  524.     cg.mapRestart = qtrue;
  525.  
  526.     // Make sure the weapon selection menu isnt up
  527.     cg.weaponMenuUp = qfalse;
  528.  
  529.     cg.gametypeStarted = qfalse;
  530.  
  531. //    cgs.media.mAutomap = 0;            // make sure to re-upload the auto-map
  532.  
  533.     CG_StartMusic(qtrue);
  534.  
  535.     trap_S_ClearLoopingSounds(qtrue);
  536.  
  537.     trap_Cvar_Set("cg_thirdPerson", "0");
  538. }
  539.  
  540. #define MAX_VOICEFILESIZE    16384
  541. #define MAX_VOICEFILES        8
  542. #define MAX_VOICECHATS        64
  543. #define MAX_VOICESOUNDS        64
  544. #define MAX_CHATSIZE        64
  545. #define MAX_HEADMODELS        64
  546.  
  547. typedef struct voiceChat_s
  548. {
  549.     char id[64];
  550.     int numSounds;
  551.     sfxHandle_t sounds[MAX_VOICESOUNDS];
  552.     char chats[MAX_VOICESOUNDS][MAX_CHATSIZE];
  553. } voiceChat_t;
  554.  
  555. typedef struct voiceChatList_s
  556. {
  557.     char name[64];
  558.     int gender;
  559.     int numVoiceChats;
  560.     voiceChat_t voiceChats[MAX_VOICECHATS];
  561. } voiceChatList_t;
  562.  
  563. typedef struct headModelVoiceChat_s
  564. {
  565.     char headmodel[64];
  566.     int voiceChatNum;
  567. } headModelVoiceChat_t;
  568.  
  569. voiceChatList_t voiceChatLists[MAX_VOICEFILES];
  570. headModelVoiceChat_t headModelVoiceChat[MAX_HEADMODELS];
  571.  
  572. /*
  573. =================
  574. CG_ParseVoiceChats
  575. =================
  576. */
  577. int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, int maxVoiceChats ) {
  578.     int    len, i;
  579.     fileHandle_t f;
  580.     char buf[MAX_VOICEFILESIZE];
  581.     const char **p, *ptr;
  582.     char *token;
  583.     voiceChat_t *voiceChats;
  584.     qboolean compress;
  585.  
  586.     compress = qtrue;
  587.     if (cg_buildScript.integer) {
  588.         compress = qfalse;
  589.     }
  590.  
  591.     len = trap_FS_FOpenFile( filename, &f, FS_READ );
  592.     if ( !f ) {
  593.         trap_Print( va( S_COLOR_RED "voice chat file not found: %s\n", filename ) );
  594.         return qfalse;
  595.     }
  596.     if ( len >= MAX_VOICEFILESIZE ) {
  597.         trap_Print( va( S_COLOR_RED "voice chat file too large: %s is %i, max allowed is %i", filename, len, MAX_VOICEFILESIZE ) );
  598.         trap_FS_FCloseFile( f );
  599.         return qfalse;
  600.     }
  601.  
  602.     trap_FS_Read( buf, len, f );
  603.     buf[len] = 0;
  604.     trap_FS_FCloseFile( f );
  605.  
  606.     ptr = buf;
  607.     p = &ptr;
  608.  
  609.     Com_sprintf(voiceChatList->name, sizeof(voiceChatList->name), "%s", filename);
  610.     voiceChats = voiceChatList->voiceChats;
  611.     for ( i = 0; i < maxVoiceChats; i++ ) {
  612.         voiceChats[i].id[0] = 0;
  613.     }
  614.     token = COM_ParseExt(p, qtrue);
  615.     if (!token || token[0] == 0) {
  616.         return qtrue;
  617.     }
  618.     if (!Q_stricmp(token, "female")) {
  619.         voiceChatList->gender = GENDER_FEMALE;
  620.     }
  621.     else if (!Q_stricmp(token, "male")) {
  622.         voiceChatList->gender = GENDER_MALE;
  623.     }
  624.     else if (!Q_stricmp(token, "neuter")) {
  625.         voiceChatList->gender = GENDER_NEUTER;
  626.     }
  627.     else {
  628.         trap_Print( va( S_COLOR_RED "expected gender not found in voice chat file: %s\n", filename ) );
  629.         return qfalse;
  630.     }
  631.  
  632.     voiceChatList->numVoiceChats = 0;
  633.     while ( 1 ) {
  634.         token = COM_ParseExt(p, qtrue);
  635.         if (!token || token[0] == 0) {
  636.             return qtrue;
  637.         }
  638.         Com_sprintf(voiceChats[voiceChatList->numVoiceChats].id, sizeof( voiceChats[voiceChatList->numVoiceChats].id ), "%s", token);
  639.         token = COM_ParseExt(p, qtrue);
  640.         if (Q_stricmp(token, "{")) {
  641.             trap_Print( va( S_COLOR_RED "expected { found %s in voice chat file: %s\n", token, filename ) );
  642.             return qfalse;
  643.         }
  644.         voiceChats[voiceChatList->numVoiceChats].numSounds = 0;
  645.         while(1) {
  646.             token = COM_ParseExt(p, qtrue);
  647.             if (!token || token[0] == 0) {
  648.                 return qtrue;
  649.             }
  650.             if (!Q_stricmp(token, "}"))
  651.                 break;
  652.             voiceChats[voiceChatList->numVoiceChats].sounds[voiceChats[voiceChatList->numVoiceChats].numSounds] = 
  653.           trap_S_RegisterSound( token );
  654.             token = COM_ParseExt(p, qtrue);
  655.             if (!token || token[0] == 0) {
  656.                 return qtrue;
  657.             }
  658.             Com_sprintf(voiceChats[voiceChatList->numVoiceChats].chats[
  659.                             voiceChats[voiceChatList->numVoiceChats].numSounds], MAX_CHATSIZE, "%s", token);
  660.             voiceChats[voiceChatList->numVoiceChats].numSounds++;
  661.             if (voiceChats[voiceChatList->numVoiceChats].numSounds >= MAX_VOICESOUNDS)
  662.                 break;
  663.         }
  664.         voiceChatList->numVoiceChats++;
  665.         if (voiceChatList->numVoiceChats >= maxVoiceChats)
  666.             return qtrue;
  667.     }
  668.     return qtrue;
  669. }
  670.  
  671. /*
  672. =================
  673. CG_LoadVoiceChats
  674. =================
  675. */
  676. void CG_LoadVoiceChats( void ) 
  677. {
  678.     int size;
  679.  
  680.     size = trap_MemoryRemaining();
  681.     CG_ParseVoiceChats( "scripts/female1.voice", &voiceChatLists[0], MAX_VOICECHATS );
  682.     CG_ParseVoiceChats( "scripts/male1.voice", &voiceChatLists[1], MAX_VOICECHATS );
  683. }
  684.  
  685. /*
  686. =================
  687. CG_GetVoiceChat
  688. =================
  689. */
  690. int CG_GetVoiceChat( voiceChatList_t *voiceChatList, const char *id, sfxHandle_t *snd, char **chat) 
  691. {
  692.     int i, rnd;
  693.  
  694.     for ( i = 0; i < voiceChatList->numVoiceChats; i++ ) 
  695.     {
  696.         if ( !Q_stricmp( id, voiceChatList->voiceChats[i].id ) ) 
  697.         {
  698.             rnd   = random() * voiceChatList->voiceChats[i].numSounds;
  699.             *snd  = voiceChatList->voiceChats[i].sounds[rnd];
  700.             *chat = voiceChatList->voiceChats[i].chats[rnd];
  701.             return qtrue;
  702.         }
  703.     }
  704.  
  705.     return qfalse;
  706. }
  707.  
  708. /*
  709. =================
  710. CG_VoiceChatListForClient
  711. =================
  712. */
  713. voiceChatList_t *CG_VoiceChatListForClient( int clientNum ) 
  714. {
  715.     clientInfo_t *ci;
  716.  
  717.     ci = &cgs.clientinfo[ clientNum ];
  718.  
  719.     switch ( ci->gender )
  720.     {
  721.         case GENDER_FEMALE:
  722.             return &voiceChatLists[0];
  723.  
  724.         case GENDER_MALE:
  725.             return &voiceChatLists[1];
  726.     }
  727.  
  728.     // just return the male voice chat list since there are more male characters
  729.     return &voiceChatLists[1];
  730. }
  731.  
  732. #define MAX_VOICECHATBUFFER        32
  733.  
  734. typedef struct bufferedVoiceChat_s
  735. {
  736.     int clientNum;
  737.     sfxHandle_t snd;
  738.     int voiceOnly;
  739.     char cmd[MAX_SAY_TEXT];
  740.     char message[MAX_SAY_TEXT];
  741. } bufferedVoiceChat_t;
  742.  
  743. bufferedVoiceChat_t voiceChatBuffer[MAX_VOICECHATBUFFER];
  744.  
  745. /*
  746. =================
  747. CG_PlayVoiceChat
  748. =================
  749. */
  750. void CG_PlayVoiceChat( bufferedVoiceChat_t *vchat ) 
  751. {
  752.     // if we are going into the intermission, don't start any voices
  753.     if ( cg.intermissionStarted ) 
  754.     {
  755.         return;
  756.     }
  757.  
  758.     if ( cg_voiceRadio.integer )
  759.     {
  760.         trap_S_StartLocalSound( vchat->snd, CHAN_VOICE);
  761.     }
  762.  
  763.     if (!vchat->voiceOnly && !cg_noVoiceText.integer) 
  764.     {
  765.         CG_AddChatText ( vchat->clientNum, vchat->message );
  766.     }
  767.  
  768.     voiceChatBuffer[cg.voiceChatBufferOut].snd = 0;
  769. }
  770.  
  771. /*
  772. =====================
  773. CG_PlayBufferedVoieChats
  774. =====================
  775. */
  776. void CG_PlayBufferedVoiceChats( void ) 
  777. {
  778.     if ( cg.voiceChatTime < cg.time ) 
  779.     {
  780.         if (cg.voiceChatBufferOut != cg.voiceChatBufferIn && voiceChatBuffer[cg.voiceChatBufferOut].snd) 
  781.         {
  782.             //
  783.             CG_PlayVoiceChat(&voiceChatBuffer[cg.voiceChatBufferOut]);
  784.             //
  785.             cg.voiceChatBufferOut = (cg.voiceChatBufferOut + 1) % MAX_VOICECHATBUFFER;
  786.             cg.voiceChatTime = cg.time + 1000;
  787.         }
  788.     }
  789. }
  790.  
  791. /*
  792. =====================
  793. CG_AddBufferedVoiceChat
  794. =====================
  795. */
  796. void CG_AddBufferedVoiceChat( bufferedVoiceChat_t *vchat ) 
  797. {
  798.     // if we are going into the intermission, don't start any voices
  799.     if ( cg.intermissionStarted ) 
  800.     {
  801.         return;
  802.     }
  803.  
  804.     memcpy(&voiceChatBuffer[cg.voiceChatBufferIn], vchat, sizeof(bufferedVoiceChat_t));
  805.     cg.voiceChatBufferIn = (cg.voiceChatBufferIn + 1) % MAX_VOICECHATBUFFER;
  806.     if (cg.voiceChatBufferIn == cg.voiceChatBufferOut) 
  807.     {
  808.         CG_PlayVoiceChat( &voiceChatBuffer[cg.voiceChatBufferOut] );
  809.         cg.voiceChatBufferOut++;
  810.     }
  811. }
  812.  
  813. /*
  814. =================
  815. CG_VoiceChatLocal
  816. =================
  817. */
  818. void CG_VoiceChatLocal( qboolean voiceOnly, int clientNum, const char* chatprefix, const char *cmd ) 
  819. {
  820.     char                *chat;
  821.     voiceChatList_t        *voiceChatList;
  822.     sfxHandle_t            snd;
  823.     bufferedVoiceChat_t vchat;
  824.  
  825.     // if we are going into the intermission, don't start any voices
  826.     if ( cg.intermissionStarted ) 
  827.     {
  828.         return;
  829.     }
  830.  
  831.     // Get the voice chat info for the speaking client
  832.     voiceChatList = CG_VoiceChatListForClient( clientNum );
  833.     if ( !CG_GetVoiceChat( voiceChatList, cmd, &snd, &chat ) ) 
  834.     {
  835.         return;
  836.     }
  837.  
  838.     vchat.clientNum = clientNum;
  839.     vchat.snd = snd;
  840.     vchat.voiceOnly = voiceOnly;
  841.     Q_strncpyz(vchat.cmd, cmd, sizeof(vchat.cmd));
  842.  
  843.     Com_sprintf ( vchat.message, sizeof(vchat.message), "%s%s", chatprefix, chat );
  844.  
  845.     CG_AddBufferedVoiceChat(&vchat);
  846. }
  847.  
  848. /*
  849. =================
  850. CG_VoiceChat
  851. =================
  852. */
  853. void CG_VoiceChat( int mode ) 
  854. {
  855.     char cmd[MAX_QPATH];
  856.     const char *chatprefix;
  857.     qboolean   voiceOnly;
  858.     int           clientNum;
  859.  
  860.     voiceOnly  = atoi(CG_Argv(1));
  861.     clientNum  = atoi(CG_Argv(2));
  862.     Com_sprintf ( cmd, MAX_QPATH, CG_Argv(4) );
  863.     chatprefix = CG_Argv(3);
  864.  
  865.     CG_VoiceChatLocal ( voiceOnly, clientNum, chatprefix, cmd );
  866. }
  867.  
  868. /*
  869. =================
  870. CG_RemoveChatEscapeChar
  871. =================
  872. */
  873. static void CG_RemoveChatEscapeChar( char *text ) {
  874.     int i, l;
  875.  
  876.     l = 0;
  877.     for ( i = 0; text[i]; i++ ) {
  878.         if (text[i] == '\x19')
  879.             continue;
  880.         text[l++] = text[i];
  881.     }
  882.     text[l] = '\0';
  883. }
  884.  
  885. /*
  886. =================
  887. CG_ServerCommand
  888.  
  889. The string has been tokenized and can be retrieved with
  890. Cmd_Argc() / Cmd_Argv()
  891. =================
  892. */
  893. static void CG_ServerCommand( void ) {
  894.     const char    *cmd;
  895.     char        text[MAX_SAY_TEXT];
  896.  
  897.     cmd = CG_Argv(0);
  898.  
  899.     if ( !cmd[0] ) {
  900.         // server claimed the command
  901.         return;
  902.     }
  903.  
  904.     if ( !strcmp( cmd, "cp" ) ) 
  905.     {
  906.         const char* s;
  907.  
  908.         s = CG_Argv(1);
  909.         if ( *s == '@' )
  910.         {
  911.             s++;
  912.         }
  913.         else
  914.         {
  915.             Com_Printf ( "@%s", s );
  916.         }
  917.  
  918.         CG_CenterPrint( s, 0.43f );        
  919.         return;
  920.     }
  921.  
  922.     if ( !strcmp( cmd, "cs" ) ) 
  923.     {
  924.         CG_ConfigStringModified();
  925.         return;
  926.     }
  927.  
  928.     if ( !strcmp( cmd, "print" ) ) 
  929.     {
  930.         Com_Printf( "%s", CG_Argv(1) );
  931.  
  932. /*
  933.         cmd = CG_Argv(1);            // yes, this is obviously a hack, but so is the way we hear about
  934.                                     // votes passing or failing
  935.         if ( !Q_stricmpn( cmd, "vote failed", 11 ) ) 
  936.         {
  937.             trap_S_StartLocalSound( cgs.media.voteFailed, CHAN_ANNOUNCER );
  938.         } 
  939.         else if ( !Q_stricmpn( cmd, "vote passed", 11 ) ) 
  940.         {
  941.             trap_S_StartLocalSound( cgs.media.votePassed, CHAN_ANNOUNCER );
  942.         }
  943. */
  944.         return;
  945.     }
  946.  
  947.     if ( !strcmp( cmd, "chat" ) ) 
  948.     {
  949.         if ( !cg_teamChatsOnly.integer ) 
  950.         {
  951.             trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
  952.             Q_strncpyz( text, CG_Argv(2), MAX_SAY_TEXT );
  953.             CG_RemoveChatEscapeChar( text );
  954.             CG_AddChatText ( atoi(CG_Argv(1)), text );
  955.             Com_Printf( "@%s\n", text );
  956.         }
  957.         return;
  958.     }
  959.  
  960.     if ( !strcmp( cmd, "tchat" ) ) 
  961.     {
  962.         trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
  963.         Q_strncpyz( text, CG_Argv(2), MAX_SAY_TEXT );
  964.         CG_RemoveChatEscapeChar( text );
  965.         CG_AddChatText ( atoi(CG_Argv(1)), text );
  966.         Com_Printf( "@%s\n", text );
  967.         return;
  968.     }
  969.     
  970.     if ( !strcmp( cmd, "vglobal" ) ) 
  971.     {
  972.         char                *chat;
  973.         voiceChatList_t        *voiceChatList;
  974.         sfxHandle_t            snd;
  975.         int                    clientNum;
  976.  
  977.         if ( !cg_voiceGlobal.integer )
  978.         {
  979.             return;
  980.         }
  981.  
  982.         clientNum = atoi(CG_Argv(1));
  983.  
  984.         // Get the voice chat info for the speaking client
  985.         voiceChatList = CG_VoiceChatListForClient( clientNum );
  986.         if ( !CG_GetVoiceChat( voiceChatList, CG_Argv(2), &snd, &chat ) ) 
  987.         {
  988.             return;
  989.         }
  990.  
  991.         trap_S_StartSound ( NULL, clientNum, CHAN_AUTO, snd, 240, 1150 );
  992.         return;
  993.     }
  994.  
  995.     if ( !strcmp( cmd, "vtchat" ) ) 
  996.     {
  997.         CG_VoiceChat( SAY_TEAM );
  998.         return;
  999.     }
  1000.  
  1001.     if ( !strcmp( cmd, "vtell" ) ) 
  1002.     {
  1003.         CG_VoiceChat( SAY_TELL );
  1004.         return;
  1005.     }
  1006.  
  1007.     if ( !strcmp( cmd, "scores" ) ) 
  1008.     {
  1009.         CG_ParseScores();
  1010.         CG_UpdateTeamCountCvars ( );
  1011.         return;
  1012.     }
  1013.  
  1014.     if ( !strcmp( cmd, "map_restart" ) ) 
  1015.     {
  1016.         CG_MapRestart( qfalse );
  1017.         return;
  1018.     }
  1019.  
  1020.     if ( Q_stricmp (cmd, "remapShader") == 0 ) 
  1021.     {
  1022.         if (trap_Argc() == 4) 
  1023.         {
  1024.             trap_R_RemapShader(CG_Argv(1), CG_Argv(2), CG_Argv(3));
  1025.         }
  1026.     }
  1027.  
  1028.     // loaddeferred can be both a servercmd and a consolecmd
  1029.     if ( !strcmp( cmd, "loaddeferred" ) ) 
  1030.     {
  1031.         CG_LoadDeferredPlayers();
  1032.         return;
  1033.     }
  1034.  
  1035.     // clientLevelShot is sent before taking a special screenshot for
  1036.     // the menu system during development
  1037.     if ( !strcmp( cmd, "clientLevelShot" ) ) 
  1038.     {
  1039.         cg.levelShot = qtrue;
  1040.         return;
  1041.     }
  1042.  
  1043.     Com_Printf( "Unknown client game command: %s\n", cmd );
  1044. }
  1045.  
  1046.  
  1047. /*
  1048. ====================
  1049. CG_ExecuteNewServerCommands
  1050.  
  1051. Execute all of the server commands that were received along
  1052. with this this snapshot.
  1053. ====================
  1054. */
  1055. void CG_ExecuteNewServerCommands( int latestSequence ) {
  1056.     while ( cgs.serverCommandSequence < latestSequence ) {
  1057.         if ( trap_GetServerCommand( ++cgs.serverCommandSequence ) ) {
  1058.             CG_ServerCommand();
  1059.         }
  1060.     }
  1061. }
  1062.